<template>
  <el-dialog
    :visible.sync="dialogVisible"
    title="基础字段编辑"
    :close-on-click-modal="false"
    @close="dialogVisible=false"
  >
    <el-form ref="forbidForm" :rules="rules" :model="formData" label-width="120px">
      <el-form-item label="字段名" prop="fieldName">
        <el-input
          v-model="formData.fieldName"
          placeholder="输入字段名"
        ></el-input>
      </el-form-item>
      <el-form-item label="字段描述" prop="desc">
        <el-input
          v-model="formData.desc"
          placeholder="字段描述会在使用字段的地方展示"
        ></el-input>
      </el-form-item>
      <el-form-item label="数据归属日期偏移" prop="dateOffset">
        <el-tooltip
          content="只能为正数，默认为0. 例如次留人数是属于前一天的数据，但是需要根据当天的数据进行计算, 那么偏移值为1. 在实际计算时，会将次留数据填入到前一天的数据条目中."
        >
          <el-input
            v-model="formData.dateOffset"
            type="number"
            min="0"
            step="1"
            placeholder="偏移值"
          ></el-input>
        </el-tooltip>
      </el-form-item>
      <el-form-item size="mini" prop="conditions">
        <span slot="label">
          <el-tooltip
            content="用于筛选参与计算的的用户数据, 例如计算留存，需要筛选出前一天注册的用户"
          >
            <span>用户筛选条件</span>
          </el-tooltip>
        </span>
        <div v-if="conditionTree.length > 0">
          <el-tree
            :data="conditionTree"
            :props="defaultProps"
            default-expand-all
            :expand-on-click-node="false"
          >
            <span slot-scope="{ node, data}" class="custom-tree-node">
              <div v-if="data.editFlag && data.editFlag === 'field'">
                <el-tooltip class="item">
                  <div slot="content">
                    选取相对天数范围起始值 <br>
                    例如: <br>
                    计算留存时需要筛选出后一天的活跃用户, 相对天数为1
                    注意: <br>
                    如果 数据归属日期偏移 > 0 <br>
                    筛选条件的偏移量是以归属日期偏移之后的日期作为基准来计算的 <br><br>
                    以次留为例: <br>
                    数据归属日期偏移需要设置为1, 既计算的是前一天的数据 <br>
                    此时需要筛选出当天活跃的用户id, 那么需要筛选  1天后-用户id-存在 的记录 <br>
                  </div>
                  <el-input
                    v-model="data.val.start"
                    type="number"
                    step="1"
                    placeholder="n天后"
                    style="width: 80px"
                  ></el-input>
                </el-tooltip>
                ~
                <el-tooltip class="item" effect="dark" content="选取相对天数范围终止值, 可以为空，">
                  <el-input
                    v-model="data.val.end"
                    type="number"
                    step="1"
                    placeholder="n天后"
                    style="width: 80px"
                  ></el-input>
                </el-tooltip>
                <el-select
                  v-model="data.val.field"
                  filterable
                  size="mini"
                  style="width: 100px"

                  placeholder="字段"
                >
                  <el-option
                    v-for="(item,i) in fields"
                    :key="i"
                    :label="item.desc"
                    :value="item.mongoField"
                  ></el-option>
                </el-select>
                <el-select
                  v-model="data.val.comparator"
                  filterable
                  style="width: 80px"
                  placeholder="计算方法"
                >
                  <el-option label=">" value=">"></el-option>
                  <el-option label=">=" value=">="></el-option>
                  <el-option label="<" value="<"></el-option>
                  <el-option label="<=" value="<="></el-option>
                  <el-option label="=" value="="></el-option>
                  <el-option label="存在" value="$exists"></el-option>
                </el-select>
                <el-input
                  v-model="data.val.value"
                  placeholder="值"
                  style="width: 50px"
                ></el-input>
                的用户
                <el-button
                  type="primary"
                  size="mini"

                  @click="confirmFieldAdd(data)"
                >
                  确认
                </el-button>
                <el-button
                  type="danger"
                  size="mini"
                  @click="() => removeNode(node, data)"
                >
                  删除
                </el-button>
              </div>
              <div v-else-if="data.editFlag && data.editFlag === 'group'">
                <el-select
                  v-model="data.label"
                  size="mini"
                  filterable
                  placeholder="逻辑计算方法"
                >
                  <el-option label="且" value="$and"></el-option>
                  <el-option label="或" value="$or"></el-option>
                  <el-option label="减" value="$minus"></el-option>

                </el-select>
                <el-button
                  type="primary"
                  size="mini"

                  @click="confirmFieldAdd(data)"
                >
                  确认
                </el-button>
                <el-button
                  type="danger"
                  size="mini"
                  @click="() => removeNode(node, data)"
                >
                  删除
                </el-button>
              </div>
              <div v-else-if="data.label">
                <span>{{ data | nodeDisplay }}</span>
                <span>
                  <el-button
                    v-if="data.label.includes('$')"
                    type="text"
                    size="mini"
                    @click="() => appendGroup(data)"
                  >
                    添加字段组
                  </el-button>
                  <el-button
                    v-if="data.label.includes('$')"
                    type="text"
                    size="mini"
                    @click="() => appendField(data)"
                  >
                    添加字段
                  </el-button>

                  <el-button
                    type="text"
                    size="mini"
                    @click="() => removeNode(node, data)"
                  >
                    删除
                  </el-button>
                </span>
              </div>

            </span>
          </el-tree>
        </div>
        <div v-else>
          <el-button
            type="primary"
            @click="appendGroup(null)"
          >
            添加字段组
          </el-button>
          <el-button
            type="primary"
            @click="appendField(null)"
          >
            添加字段
          </el-button>
        </div>
      </el-form-item>
      <el-form-item size="mini" label="统计字段" prop="relyOn">
        <span v-for="(item, i) in relyOnList" :key="i">
          <span>{{ item | relyOnDisplay }}</span>
          <el-button
            type="primary"
            @click="removeRelyOn(item)"
          >删除</el-button>
          <br>
        </span>
        <el-select
          v-model="relyOnToAdd.field"
          filterable
          placeholder="选择字段"
        >
          <el-option
            v-for="(item,i) in fields"
            :key="i"
            :label="item.desc"
            :value="item.mongoField"
          ></el-option>
        </el-select>
        <el-select
          v-if="relyOnToAdd.field && (fieldTypeMap[relyOnToAdd.field] === 'int' || fieldTypeMap[relyOnToAdd.field] === 'float')"
          v-model="relyOnToAdd.method"
          filterable
          placeholder="统计方法"
        >
          <el-option
            label="加和"
            value="sum"
          ></el-option>
          <el-option
            label="平均值"
            value="mean"
          ></el-option>
          <el-option
            label="按时间正序第一个值"
            value="first"
          ></el-option>
          <el-option
            label="按时间正序最后一个值"
            value="last"
          ></el-option>
          <el-option
            label="值的个数"
            value="nunique"
          ></el-option>
          <el-option
            label="记录个数"
            value="count"
          ></el-option>
        </el-select>
        <el-select
          v-else
          v-model="relyOnToAdd.method"
          filterable
          placeholder="统计方法"
        >
          <el-option
            label="按时间正序第一个值"
            value="first"
          ></el-option>
          <el-option
            label="按时间正序最后一个值"
            value="last"
          ></el-option>
          <el-option
            label="值的个数"
            value="nunique"
          ></el-option>
          <el-option
            label="记录个数"
            value="count"
          ></el-option>
        </el-select>
        <el-button
          type="primary"
          @click="addRelyOn"
        >添加
        </el-button>
      </el-form-item>

      <el-form-item label="可用参数" prop="">
        <span
          v-for="(item, i) in relyOnList"
          :key="i"
        >{{ item.display + ':#' + item.field + '#' }}<br></span>
      </el-form-item>
      <el-form-item label="计算公式" prop="formula">
        <el-input
          v-model="formData.formula"
          placeholder="基础字段的计算公式(只能使用可用参数中罗列的参数, 格式是#xxxx#, 支持正常的算数计算)"
        ></el-input>
      </el-form-item>
      <el-form-item v-if="!formData.id" prop="addDefaultDisplayField" label="同时添加同名显示字段">
        <el-checkbox v-model="formData.addDefaultDisplayField"></el-checkbox>
      </el-form-item>
    </el-form>
    <span slot="footer">
      <el-button @click="dialogVisible=false">取消</el-button>
      <el-button type="primary" @click="onOK">确定</el-button>
    </span>
  </el-dialog>
</template>
<script>
import { dictList } from '@/api/bx';

export default {
  filters: {
    nodeDisplay(node) {
      if (node.label.includes('$')) {
        const logicOp = {
          '$and': '且',
          '$or': '或',
          '$minus': '减'
        }
        return logicOp[node.label]
      } else {
        let comparator = node.val.comparator
        let value = node.val.value
        const start = node.val.start
        const end = node.val.end
        if (comparator === '$exists') {
          comparator = '存在'
          value = ''
        }
        const tail = node.val.field + ' ' + comparator + ' ' + value
        if (start > 0) {
          return start + '天后～' + end + '天后 ' + tail
        } else if (start < 0) {
          if (end > 0) {
            return Math.abs(start) + '天前～' + end + '天后 ' + tail
          } else if (end === '0') {
            return Math.abs(start) + '天前～当日 ' + tail
          } else if (!end) {
            return Math.abs(start) + '天前 ' + tail
          } else {
            return Math.abs(start) + '天前～' + Math.abs(end) + '天前 ' + tail
          }
        } else if (end > 0) {
          return end + '天后 ' + tail
        } else {
          return tail
        }
      }
    },
    relyOnDisplay(item) {
      const methods = {
        sum: '加和',
        mean: '平均值',
        first: '按时间正序的第一个值',
        last: '按时间正序的最后一个值',
        nunique: '值的个数',
        count: '记录数量'
      }
      return item.display + '-' + methods[item.method]
    }

  },
  props: {
    visible: {
      type: Boolean,
      default: false
    },
    value: {
      type: Object,
      default: null
    }
  },
  data() {
    return {
      formData: {},
      conditionTree: [],
      relyOnToAdd: {
        field: '',
        method: ''
      },
      fields: [],
      fieldDisplayMap: {},
      fieldTypeMap: {},
      rules: {
        fieldName: [{ required: true, message: '不能为空' }],
        desc: [{ required: true, message: '不能为空' }],
        dateOffset: [{ required: true, message: '不能为空' }],
        formula: [{ required: true, message: '不能为空' }]
      },
      defaultProps: {
        children: 'children',
        label: 'label'
      }
    }
  },
  computed: {
    dialogVisible: {
      get() {
        return this.visible
      },
      set(val) {
        this.$emit('update:visible')
      }
    },
    relyOnList: {
      get() {
        const result = []
        for (const i in this.formData.relyOn) {
          let display = this.fieldDisplayMap[i]
          if (!display) {
            display = i
          }
          result.push({ field: i, method: this.formData.relyOn[i], display: display })
        }
        return result
      },
      set(val) {
        const result = {}
        val.forEach(item => {
          result[item.field] = item.method
        })
        this.formData.relyOn = result
      }
    }
  },
  watch: {
    visible(val) {
      if (val) {
        this.formData = this.value
        const conditionTree = this.extractConditionToTree(this.value.conditions)
        if (conditionTree) {
          this.conditionTree = [conditionTree]
        } else {
          this.conditionTree = []
        }
      }
    }
  },
  created() {
    this.getDict()
  },
  methods: {
    getDict() {
      dictList().then(resp => {
        this.fields = resp.data.list
        const map = {}
        const displayMap = {}
        this.fields.forEach(item => {
          map[item['mongoField']] = item['pythonType']
          displayMap[item['mongoField']] = item['desc']
        })
        this.fieldTypeMap = map
        this.fieldDisplayMap = displayMap
      })
    },
    extractConditionToTree(conditions) {
      if (!conditions) {
        return null;
      }
      let isLogicObj = false
      let firstKey = ''
      for (const key in conditions) {
        if (key.includes('$')) {
          isLogicObj = true
          firstKey = key
          break
        }
      }
      if (isLogicObj) {
        const data = []
        conditions[firstKey].forEach(item => {
          data.push(this.extractConditionToTree(item))
        })
        return {
          label: firstKey,
          editFlag: conditions.editFlag,
          children: data
        }
      } else {
        return { label: conditions.field, val: conditions }
      }
    },

    formatConditions(conditions) {
      if (!conditions || conditions.editFlag) {
        return null
      }
      if (conditions.label.includes('$')) {
        const children = []
        conditions.children.forEach(child => {
          const childResult = this.formatConditions(child)
          if (childResult) {
            children.push(childResult)
          }
        })
        if (children.length > 0) {
          const result = {}
          result[conditions.label] = children
          return result
        } else {
          return null
        }
      } else {
        return conditions.val
      }
    },

    maxConditionOffset(conditions) {
      if (!conditions) {
        return 0
      }
      let children = null
      for (const key in conditions) {
        if (key.includes('$')) {
          children = conditions[key]
          break
        }
      }
      if (children) {
        let max = 0
        children.forEach(child => {
          const result = this.maxConditionOffset(child)
          if (result > max) {
            max = result
          }
        })
        return max
      } else {
        return Math.max(conditions.start, conditions.end)
      }
    },

    onOK() {
      this.$refs.forbidForm.validate(valid => {
        if (valid) {
          if (!this.formData.formula) {
            this.$message.error('请填入公式')
            return
          }
          if (this.conditionTree && this.conditionTree.length > 0) {
            this.formData.conditions = this.formatConditions(this.conditionTree[0])
            // if (this.maxConditionOffset(this.formData.conditions) > this.formData.dateOffset) {
            //   this.$message.error('筛选条件中出现时间偏移量 > 归属日期偏移的情况')
            //   return
            // }
          }
          this.$emit('success', this.formData)
        }
      })
    },

    appendGroup(data) {
      if (data) {
        data.children.push(
          {
            label: '',
            editFlag: 'group',
            children: []
          }
        )
      } else {
        this.conditionTree = [
          {
            label: '',
            editFlag: 'group',
            children: []
          }
        ]
      }
    },

    appendField(data) {
      if (data) {
        data.children.push(
          {
            editFlag: 'field',
            parent: data,
            label: '',
            val: {
              field: '',
              comparator: '',
              value: ''
            }
          }
        )
      } else {
        this.conditionTree = [{
          editFlag: 'field',
          label: '',
          val: {
            field: '',
            comparator: '',
            value: ''
          }
        }]
      }
    },

    confirmFieldAdd(data) {
      if (data.editFlag === 'field') {
        if (!data.val.field || !data.val.comparator) {
          this.$message.error('字段/计算方法不能为空')
          return
        }
        if (data.val.comparator !== '$exists' && !data.val.value) {
          this.$message.error('值不能为空')
          return
        }
        if (data.val.start && data.val.end) {
          if (data.val.start > data.val.end) {
            this.$message.error('时间范围选择错误，起始时间需要小于结束时间')
            return
          }
        } else if (data.val.start) {
          if (data.val.start > 0) {
            data.val.end = data.val.start
            data.val.start = null
          }
        } else {
          if (data.val.end < 0) {
            data.val.start = data.val.end
            data.val.end = null
          }
        }
        data.editFlag = null
        data.label = data.val.field
        data.val.editFlag = null
      } else {
        if (!data.label) {
          this.$message.error('请选择逻辑计算方法')
          return;
        }
        data.editFlag = null
        console.log('append impl:', data)
        console.log(this.conditionTree)
      }
      this.conditionTree = Object.assign([], this.conditionTree)
    },

    removeNode(node, data) {
      if (node.parent.data instanceof Array) {
        const index = node.parent.data.indexOf(data)
        if (index > -1) {
          this.conditionTree = []
        }
      } else {
        const index = node.parent.data.children.indexOf(data)
        if (index > -1) {
          node.parent.data.children.splice(index, 1)
        }
      }
    },

    removeCondition(conditions, field) {
      if (conditions.children) {
        conditions.children.forEach((item, i, object) => {
          // this.removeCondition(item)
          if (item.children) {
            this.removeCondition(item, field)
          } else if (conditions.label === field) {
            object.splice(i, 1)
          }
        })
      } else if (conditions.label === field) {
        this.conditionTree = []
      }
    },

    removeRelyOn(item) {
      delete this.formData.relyOn[item.field]
      this.formData.relyOn = Object.assign({}, this.formData.relyOn)
    },

    addRelyOn() {
      if (!this.relyOnToAdd.field) {
        this.$message.error('需要选择依赖的字段')
        return
      }
      if (!this.relyOnToAdd.method) {
        this.$message.error('需要选择字段的集合方法')
        return
      }
      if (!this.formData.relyOn) {
        this.formData.relyOn = {}
      }
      this.formData.relyOn[this.relyOnToAdd.field] = this.relyOnToAdd.method
      this.relyOnToAdd = { field: null, method: '' }
      this.formData.relyOn = Object.assign({}, this.formData.relyOn)
    }
  }
}
</script>
